package org.awa.demo.opencv.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.awa.demo.opencv.constant.CRSConstant;
import org.awa.demo.opencv.demo.FaceDetection;
import org.awa.demo.opencv.entity.*;
import org.awa.demo.opencv.gis.GeoToolsUtils;
import org.awa.demo.opencv.gis.bean.BuildingOrder;
import org.awa.demo.opencv.gis.bean.RoadOrder;
import org.awa.demo.opencv.mapper.*;
import org.awa.demo.opencv.request.CalBuildRequest;
import org.awa.demo.opencv.request.CalRequest;
import org.awa.demo.opencv.request.TrafficAddRequest;
import org.awa.demo.opencv.service.IGisService;
import org.awa.demo.opencv.service.IOpenCvService;
import org.awa.demo.opencv.sound.SoundUtil;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.store.ContentFeatureCollection;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geojson.feature.FeatureJSON;
import org.geotools.geojson.geom.GeometryJSON;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.referencing.CRS;
import org.geotools.referencing.ReferencingFactoryFinder;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.operation.distance.DistanceOp;
import org.opengis.feature.Property;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.referencing.crs.CRSFactory;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.*;
import java.rmi.ServerException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author xxf
 */
@Slf4j
@Service
public class GisServiceImpl implements IGisService {

    @Autowired
    private GeoToolsUtils geoToolsUtils;

    @Autowired
    private SoundUtil soundUtil;

    @Autowired
    private IOpenCvService openCvService;

    @Resource
    private GeoPolygonBuildMapper geoPolygonBuildMapper;

    @Resource
    private GeoPolygonRoadMapper geoPolygonRoadMapper;

    @Resource
    private TrafficLogMapper trafficLogMapper;

    @Resource
    private SoundCalLogMapper soundCalLogMapper;

//    @Override
//    public List<GeoPolygonBuildEntity> importGisBuilding(MultipartFile file) throws Exception{
//        File fileLocal = getFileFromMulti(file);
//
//        ContentFeatureCollection featureList = geoToolsUtils.readSHP(fileLocal);
//        SimpleFeatureIterator featureIterator = featureList.features();
//
//        List<GeoPolygonBuildEntity> entityList = new ArrayList<>();
//
//        while (featureIterator.hasNext()){
//            SimpleFeature feature = featureIterator.next();
//
//            GeoPolygonBuildEntity itemDao = new GeoPolygonBuildEntity();
//            itemDao.setDelFlag("0");
//            Iterator<? extends Property> iterator = feature.getValue().iterator();
//            while (iterator.hasNext()){
//                Property property = iterator.next();
//                String name = property.getName().toString();
//                log.info("{}:{}",name,String.valueOf(property.getValue()));
//                if("gml_id".equals(name)){
//                    itemDao.setGeoId((String) property.getValue());
//                }else if("the_geom".equals(name)){
//                    MultiPolygon geom = (MultiPolygon) property.getValue();
//                    itemDao.setGeom(geom.toString());
//                }else if("height".equals(name)){
//                    itemDao.setHeight((Double) property.getValue());
//                }
//            }
//            entityList.add(itemDao);
//        }
//
//        fileLocal.delete();
//
//        return entityList;
//    }
//
//    @Override
//    public List<GeoPolygonRoadEntity> importGisRoad(MultipartFile file) throws Exception{
//        File fileLocal = getFileFromMulti(file);
//
//        ContentFeatureCollection featureList = geoToolsUtils.readSHP(fileLocal);
//        SimpleFeatureIterator featureIterator = featureList.features();
//
//        List<GeoPolygonRoadEntity> entityList = new ArrayList<>();
//
//        while (featureIterator.hasNext()){
//            SimpleFeature feature = featureIterator.next();
//
//            GeoPolygonRoadEntity itemDao = new GeoPolygonRoadEntity();
//            itemDao.setDelFlag("0");
//            Iterator<? extends Property> iterator = feature.getValue().iterator();
//            while (iterator.hasNext()){
//                Property property = iterator.next();
//                String name = property.getName().toString();
//
//                if("gml_id".equals(name)){
//                    itemDao.setGeoId((String) property.getValue());
//                }else if("the_geom".equals(name)){
//                    MultiLineString geom = (MultiLineString) property.getValue();
//                    itemDao.setGeom(geom.toString());
//                }else if("Name".equals(name)){
//                    itemDao.setName((String) property.getValue());
//                }else if("pyname".equals(name)){
//                    itemDao.setRoad((String) property.getValue());
//                }else if("length".equals(name)){
//                    Double length = 0d;;
//                    try{
//                        length = Double.valueOf((String) property.getValue());
//                    }catch (Exception e){
//                        e.printStackTrace();
//                    }
//                    itemDao.setLength(length);
//                }else if("speedclass".equals(name)){
//                    Integer speedClass = 0;
//                    try{
//                        speedClass = Integer.valueOf((String) property.getValue());
//                    }catch (Exception e){
//                        e.printStackTrace();
//                    }
//                    itemDao.setSpeedClass(speedClass);
//                }
//            }
//            entityList.add(itemDao);
//        }
//
//        fileLocal.delete();
//
//        return entityList;
//    }

    @Override
    public void saveGeoPolygonBuildList(List<GeoPolygonBuildEntity> dataList){
        geoPolygonBuildMapper.insertBatch(dataList);
    }

    @Override
    public void saveGeoPolygonRoadList(List<GeoPolygonRoadEntity> dataList){
        geoPolygonRoadMapper.insertBatch(dataList);
    }

    private File getFileFromMulti(MultipartFile file) throws Exception{
        File fileLocal = new File("D://demotest/"+ DateUtil.format(new Date(),"yyyyMMdd") + "/" + file.getOriginalFilename());
        FileUtil.writeFromStream(file.getInputStream(),fileLocal);
        return fileLocal;
    }

    /**
     * 计算噪声地图
     *
     * @param request
     * @throws Exception
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<Integer, SoundCalBuildLogEntity> cal(CalRequest request){
        Date calTime = getCalTime();

        SoundCalLogEntity calLog = new SoundCalLogEntity();
        calLog.setDateTime(calTime);
        calLog.setDelFlag("0");
        calLog.setStartTime(new Date());
        calLog.setIsComplete(0);
        soundCalLogMapper.insert(calLog);

        Map<Integer,SoundCalBuildLogEntity> buildLogMap = new HashMap<>();

        try{
            List<TrafficLogEntity> trafficList = trafficLogMapper.findListByDate(calTime);
            if(trafficList.size() == 0){
                log.info("no traffic pref hour");
                return buildLogMap;
            }

            Map<Integer,List<TrafficLogEntity>> trafficMap = trafficList.parallelStream().collect(Collectors.groupingBy(TrafficLogEntity::getRoadId));
            Map<Integer,BuildingOrder> buildingMap = new HashMap<>();

            List<GeoPolygonRoadEntity> roadList = null;
            if(StringUtils.isEmpty(request.getRange())){
                roadList = geoPolygonRoadMapper.findAllGeoFeatureList();
            }else {
                roadList = geoPolygonRoadMapper.findGeoListByRange(request.getRange());
            }
            for (int i = 0; i < roadList.size(); i++) {
                GeoPolygonRoadEntity road = roadList.get(i);

                RoadOrder roadOrder = new RoadOrder(road);
                List<TrafficLogEntity> roadTrafficList = trafficMap.get(road.getId());
                if(roadTrafficList == null || roadTrafficList.size() == 0){
                    continue;
                }
                soundUtil.calRoadLept(roadOrder,roadTrafficList.get(0));

                List<GeoPolygonBuildEntity> buildList = geoPolygonBuildMapper.findBuildingListByRoadId(road.getId(),600);
                for (int j = 0; j < buildList.size(); j++) {
                    GeoPolygonBuildEntity build = buildList.get(j);

                    BuildingOrder buildingOrder = buildingMap.get(build.getId());
                    if(buildingOrder == null){
                        buildingOrder = new BuildingOrder(build);
                        buildingMap.put(build.getId(),buildingOrder);
                    }
                    buildingOrder.addRoad(roadOrder);
                }
            }

            Iterator<Integer> buildIterator = buildingMap.keySet().iterator();

            while (buildIterator.hasNext()){
                Integer buildId = buildIterator.next();
                BuildingOrder buildingOrder = buildingMap.get(buildId);

                soundUtil.calBuildLeqtArr(buildingOrder,2);
                SoundCalBuildLogEntity buildLog = openCvService.buildImageFromSoundArr(calLog,buildingOrder);
                buildLogMap.put(buildLog.getBuildId(),buildLog);
            }

            calLog.setIsComplete(1);
        }catch (Exception e){
            log.error("cal exception: "+e.toString(),e);
            calLog.setIsComplete(0);
            String errorMsg = e.toString();
            errorMsg = errorMsg.length() > 511 ? errorMsg.substring(0,510) : errorMsg;
            calLog.setErrMsg(errorMsg);
        }
        calLog.setEndTime(new Date());
        soundCalLogMapper.updateById(calLog);

        return buildLogMap;
    }

    /**
     * 计算噪声地图
     *
     * @param request
     * @throws Exception
     * @return
     */
    @Override
    public SoundCalBuildLogEntity calBuild(CalBuildRequest request){
        Date calTime = getCalTime();

        SoundCalLogEntity calLog = new SoundCalLogEntity();
        calLog.setDateTime(calTime);
        calLog.setDelFlag("0");
        calLog.setStartTime(new Date());
        calLog.setIsComplete(0);
        soundCalLogMapper.insert(calLog);

        SoundCalBuildLogEntity buildLog = null;

        try{
            List<TrafficLogEntity> trafficList = trafficLogMapper.findListByDate(calTime);
            if(trafficList.size() == 0){
                log.info("no traffic pref hour");
                return null;
            }
            Map<Integer,List<TrafficLogEntity>> trafficMap = trafficList.parallelStream().collect(Collectors.groupingBy(TrafficLogEntity::getRoadId));

            GeoPolygonBuildEntity buildEntity = geoPolygonBuildMapper.findBuildListByGeoId(request.getBuildGeoId());
            if(buildEntity == null){
                throw new ServerException("build is null - "+request.getBuildGeoId());
            }
            BuildingOrder buildingOrder = new BuildingOrder(buildEntity);

            List<GeoPolygonRoadEntity> roadList = geoPolygonRoadMapper.findRoadListByBuild(buildEntity.getId(),30);
            for (int i = 0; i < roadList.size(); i++) {
                GeoPolygonRoadEntity road = roadList.get(i);

                RoadOrder roadOrder = new RoadOrder(road);
                List<TrafficLogEntity> roadTrafficList = trafficMap.get(road.getId());
                if(roadTrafficList == null || roadTrafficList.size() == 0){
                    continue;
                }

                soundUtil.calRoadLept(roadOrder,roadTrafficList.get(0));
                buildingOrder.addRoad(roadOrder);
            }

            soundUtil.calBuildLeqtArr(buildingOrder,2);
            buildLog = openCvService.buildImageFromSoundArr(calLog,buildingOrder);

            calLog.setIsComplete(1);
        }catch (Exception e){
            log.error("cal exception: "+e.toString(),e);
            calLog.setIsComplete(0);
            String errorMsg = e.toString();
            errorMsg = errorMsg.length() > 511 ? errorMsg.substring(0,510) : errorMsg;
            calLog.setErrMsg(errorMsg);
        }
        calLog.setEndTime(new Date());
        soundCalLogMapper.updateById(calLog);

        return buildLog;
    }

    private Date getCalTime(){
//        Calendar calendar = Calendar.getInstance();
//        calendar.add(Calendar.HOUR,-1);
//        calendar.set(Calendar.MINUTE,0);
//        calendar.set(Calendar.SECOND,0);
//        calendar.set(Calendar.MILLISECOND,0);
//        Date calTime = calendar.getTime();
        return new Date(DateUtil.parse("2024-06-11 15:00:00","yyyy-MM-dd HH:mm:ss").getTime());
    }

    /**
     * 添加交通数据
     *
     * @param request
     * @throws Exception
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addTraffic(TrafficAddRequest request) throws Exception{

        List<GeoPolygonRoadEntity> roadList = geoPolygonRoadMapper.findListByRoad(request.getRoad());
        if(roadList.size() == 0){
            return;
        }

        TrafficLogEntity entity = new TrafficLogEntity();
        Date date = DateUtil.parse(request.getDate(),"yyyy-MM-dd HH");
        entity.setDateTime(date);
        if(request.getBigCar() != null){
            entity.setBigNum(request.getBigCar().getCarNum());
            entity.setBigRate(request.getBigCar().getCarRate());
        }

        if(request.getMiddleCar() != null){
            entity.setMiddleNum(request.getMiddleCar().getCarNum());
            entity.setMiddleRate(request.getMiddleCar().getCarRate());
        }

        if(request.getSmallCar() != null){
            entity.setSmallNum(request.getSmallCar().getCarNum());
            entity.setSmallRate(request.getSmallCar().getCarRate());
        }

        List<TrafficLogEntity> trafficLogList = new ArrayList<>();
        List<Integer> delLogList = new ArrayList<>();
        for (int i = 0; i < roadList.size(); i++) {
            Integer roadId = roadList.get(i).getId();
            List<TrafficLogEntity> existLogList = trafficLogMapper.findListByDateAndRoad(roadId,date);
            if(existLogList.size() > 0){
                delLogList.addAll(existLogList.parallelStream().map(TrafficLogEntity::getId).collect(Collectors.toList()));
            }
            trafficLogList.add(new TrafficLogEntity(roadId,entity));
        }
        if(delLogList.size() > 0){
            trafficLogMapper.deleteByIdList(delLogList);
        }
        if(trafficLogList.size() > 0){
            trafficLogMapper.insertBatch(trafficLogList);
        }
    }

    @Override
    public void buildGeoJson(Map<Integer, SoundCalBuildLogEntity> buildLogMap) {
        try{
            //创建featureType
            SimpleFeatureTypeBuilder featureTypeBuildBuilder = new SimpleFeatureTypeBuilder();
            featureTypeBuildBuilder.setName("geo-build-list");
            featureTypeBuildBuilder.add("geometry", Polygon.class);
            featureTypeBuildBuilder.add("height",String.class);
            featureTypeBuildBuilder.add("path",String.class);
            SimpleFeatureType featureBuildType = featureTypeBuildBuilder.buildFeatureType();
            //创建feature生成器
            SimpleFeatureBuilder featureBuildBuilder = new SimpleFeatureBuilder(featureBuildType);
            //创建WKT生成器
            WKTReader reader = new WKTReader(JTSFactoryFinder.getGeometryFactory());
            //创建坐标系转换器
            CoordinateReferenceSystem crsSource = CRS.parseWKT(CRSConstant.WKT_EPSG4528);
            CoordinateReferenceSystem crsTarget = CRS.decode("EPSG:4490");
            MathTransform transform = CRS.findMathTransform(crsSource,crsTarget,true);
            //GEOJSON 文件转换器
            FeatureJSON fjson = new FeatureJSON();
            List<SimpleFeature> features = new ArrayList<>();
            SimpleFeatureCollection collection = new ListFeatureCollection(featureBuildType,features);

            //建筑
            List<GeoPolygonBuildEntity> polygonBuildList = geoPolygonBuildMapper.findAllGeoFeatureList();
            for (int i = 0; i < polygonBuildList.size(); i++) {
                try{
                    GeoPolygonBuildEntity buildEntity = polygonBuildList.get(i);
                    SoundCalBuildLogEntity entity = buildLogMap.get(buildEntity.getId());
                    features.add(getBuildFeature(featureBuildBuilder,buildEntity,reader,(entity != null ? entity.getPathUrl() : null),transform));
                }catch (Exception e){
                    e.printStackTrace();
                    log.error("feature trans error:"+e.toString(),e);
                }
            }

            List<GeoPolygonRoadEntity> polygonRoadList = geoPolygonRoadMapper.findAllGeoFeatureList();
            for (int i = 0; i < polygonRoadList.size(); i++) {
                features.add(getRoadFeature(featureBuildBuilder,polygonRoadList.get(i),reader,transform));
            }

            FileWriter writer = new FileWriter(new File(FaceDetection.GEOJSON_PATH));
            fjson.writeFeatureCollection(collection,writer);

            log.info("geojson over");
        }catch (Exception e){
            e.printStackTrace();
            log.error("output geojson failed:"+e.toString(),e);
        }
    }

    @Override
    public void buildGeoJsonSingle(SoundCalBuildLogEntity entity){
        try{
            SimpleFeatureTypeBuilder featureTypeBuildBuilder = new SimpleFeatureTypeBuilder();
            featureTypeBuildBuilder.setName("geo-build-list");
            featureTypeBuildBuilder.add("geometry", Polygon.class);
            featureTypeBuildBuilder.add("height",String.class);
            featureTypeBuildBuilder.add("path",String.class);
            SimpleFeatureType featureBuildType = featureTypeBuildBuilder.buildFeatureType();
            SimpleFeatureBuilder featureBuildBuilder = new SimpleFeatureBuilder(featureBuildType);


            WKTReader reader = new WKTReader(JTSFactoryFinder.getGeometryFactory());

            CoordinateReferenceSystem crsSource = CRS.parseWKT(CRSConstant.WKT_EPSG4528);
            CoordinateReferenceSystem crsTarget = CRS.decode("EPSG:4490");
            MathTransform transform = CRS.findMathTransform(crsSource,crsTarget,true);

            FeatureJSON fjson = new FeatureJSON();
            List<SimpleFeature> features = new ArrayList<>();
            SimpleFeatureCollection collection = new ListFeatureCollection(featureBuildType,features);

            //建筑
            GeoPolygonBuildEntity buildEntity = geoPolygonBuildMapper.findBuildById(entity.getBuildId());
            features.add(getBuildFeature(featureBuildBuilder,buildEntity,reader,(buildEntity != null ? entity.getPathUrl() : null),transform));

            //道路
            List<GeoPolygonRoadEntity> polygonRoadList = geoPolygonRoadMapper.findRoadListByBuild(buildEntity.getId(),30);
            for (int i = 0; i < polygonRoadList.size(); i++) {
                features.add(getRoadFeature(featureBuildBuilder,polygonRoadList.get(i),reader,transform));
            }

            StringWriter writer = new StringWriter();
            fjson.writeFeatureCollection(collection,writer);

            log.info("geojson: "+writer.toString());
        }catch (Exception e){
            e.printStackTrace();
            log.error("aaa >>>> "+e.toString(),e);
        }
    }

    private SimpleFeature getBuildFeature(SimpleFeatureBuilder featureBuilder, GeoPolygonBuildEntity polygon, WKTReader reader, String path,MathTransform transform) throws Exception{
        MultiPolygon geometry = (MultiPolygon) reader.read(polygon.getGeom());
        geometry = (MultiPolygon) JTS.transform(geometry, transform);

        featureBuilder.add(geometry);
        featureBuilder.add(polygon.getHeight() == null ? "" : String.valueOf(polygon.getHeight()));
        featureBuilder.add(path == null ? "" : FaceDetection.FILE_UPLOAD_URL_PREF + path);
        SimpleFeature feature = featureBuilder.buildFeature(polygon.getGeoId());
        return feature;
    }

    private SimpleFeature getRoadFeature(SimpleFeatureBuilder featureBuilder, GeoPolygonRoadEntity polygon, WKTReader reader,MathTransform transform) throws Exception{
        MultiLineString geometry = (MultiLineString) reader.read(polygon.getGeom());
        geometry = (MultiLineString) JTS.transform(geometry, transform);
        featureBuilder.add(geometry);
        featureBuilder.add("");
        featureBuilder.add("");
        SimpleFeature feature = featureBuilder.buildFeature(polygon.getGeoId());
        return feature;
    }

//    public static void main(String[] args) {
//
//        try {
//            String wkt = "POINT (40514474.2589528 3356418.54543048)";
//            WKTReader reader = new WKTReader(JTSFactoryFinder.getGeometryFactory());
//            Point point = (Point) reader.read(wkt);
//
//            CoordinateReferenceSystem crsSource = CRS.parseWKT(CRSConstant.WKT_EPSG4528);
//            CoordinateReferenceSystem crsTarget = CRS.decode("EPSG:4326");
//            // 投影转换
//            MathTransform transform = CRS.findMathTransform(crsSource, crsTarget,true);
//            point = (Point) JTS.transform(point, transform);
//
//            System.out.println(point.toText());
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }

//    public static void main(String[] args) {
//
//        String linkWkt = "MultiLineString ((119.95726329306236835 30.2162476053522191, 119.95703798713655885 30.21691743378031347, 119.95699536169112775 30.21708387599577961, 119.9569852127755496 30.21752839849805738, 119.95768041349259647 30.21796277208476056, 119.95850450543746035 30.21848239656231172, 119.95945038436926211 30.21907915279825119, 119.95980762619757343 30.21930648850717915, 119.96212157894916572 30.22075169408537221, 119.96348153363651079 30.22161638169254871, 119.96445988909815128 30.22222531662718126))";
//        String pointWkt = "Point (119.95879060008860506 30.21723442421010475)";
//        GeometryFactory geometryFactory = new GeometryFactory();
//
//        WKTReader wktReader = new WKTReader(geometryFactory);
//        try {
//            Geometry line = wktReader.read(linkWkt);
//
//            // 创建一个点的几何表示
//            Geometry point = wktReader.read(pointWkt);
//
//
//            // 计算点到线的距离
//            DistanceOp distanceOp = new DistanceOp(point, line);
//            Coordinate[] coordinates = distanceOp.nearestPoints();
//
//            LineString lineString = geometryFactory.createLineString(coordinates);
//
//
//
//            // 输出距离
//            System.out.println("The distance from the point to the line is: " + lineString.toText());
//        } catch (ParseException e) {
//            e.printStackTrace();
//        }
//    }

//    public static void main(String[] args) {
//        try{
//            //gps
//            String lng = "120.016175";
//            String lat = "30.270028";
//
//            GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
//            Coordinate coordinate = new Coordinate(Double.parseDouble(lat), Double.parseDouble(lng));
//            Point point = geometryFactory.createPoint(coordinate);
//            System.out.println(">>>> 4326 : " + point.toText());
//
////            CoordinateReferenceSystem sourceCRS = CRS.decode("EPSG:4326");
////            CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:4490");
////            MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS, false);
////            Geometry point4490 = JTS.transform(point, transform);
////            System.out.println(">>>> 4490 : " + point4490.toText());
//
//            CoordinateReferenceSystem sourceCRS1 = CRS.decode("EPSG:4490");
//            CoordinateReferenceSystem targetCRS1 = CRS.decode("EPSG:4549");
//            MathTransform transform1 = CRS.findMathTransform(sourceCRS1, targetCRS1, false);
//            Geometry point4549 = JTS.transform(point, transform1);
//            System.out.println(">>>> 4549 : " + point4549.toText());
//        }catch (Exception e){
//            e.printStackTrace();
//        }
//
//
//
//
//    }

}
